﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Liang.Net
{
    /// <summary>
    /// 接收数据
    /// </summary>
    /// <param name="result"></param>
    public delegate void ReceiveDataDelegate(TCPClientResult result);

    /// <summary>
    /// 断开连接
    /// </summary>
    /// <param name="client"></param>
    public delegate void ClientDisconnectedDelegate(TCPClient client);

    /// <summary>
    /// 连接到服务端的客户端
    /// </summary>
    public sealed class TCPClient : IDisposable
    {
        TcpClient m_client = null;
        NetworkStream m_streamToClient = null;

        static readonly int BufferSize = 8192;
        byte[] m_buffer = null;

        /// <summary>
        /// 接收到的数据缓存
        /// </summary>
        List<byte> m_recvBuffer = new List<byte>();

        ReceiveDataDelegate m_doReceiveData = null;

        /// <summary>
        /// TCPServer 管理的客户端断开连接后，调用的回调函数
        /// </summary>
        ClientDisconnectedDelegate m_doDisConnect = null;

        /// <summary>
        /// 获取远程客户端IP地址
        /// </summary>
        public string IPAddr
        {
            get
            {
                return ((IPEndPoint)m_client.Client.RemoteEndPoint).ToString();
            }
        }

        /// <summary>
        /// 获取远程客户端端口号
        /// </summary>
        public int Port
        {
            get
            {
                return ((IPEndPoint)m_client.Client.RemoteEndPoint).Port;
            }
        }

        public TCPClient(string strServerIP, int nServerPort, ReceiveDataDelegate receiveDataHandler, ClientDisconnectedDelegate disConnect)
            : this(new TcpClient(strServerIP, nServerPort), receiveDataHandler, disConnect)
        {
        }

        public TCPClient(IPEndPoint ipEndPoint, ReceiveDataDelegate receiveDataHandler, ClientDisconnectedDelegate disConnect)
            : this(new TcpClient(ipEndPoint), receiveDataHandler, disConnect)
        {
        }

        public TCPClient(TcpClient client, ReceiveDataDelegate receiveDataHandler, ClientDisconnectedDelegate disConnect)
        {
            m_client = client;
            m_doReceiveData = receiveDataHandler;
            m_doDisConnect = disConnect;

            // 获得流
            m_streamToClient = client.GetStream();
            m_buffer = new byte[BufferSize];

            // 投递接收数据事件
            m_streamToClient.BeginRead(m_buffer, 0, BufferSize,
                new AsyncCallback(ReadComplete), null);

            // 开启在线测试
            Task.Factory.StartNew(CheckForConnection);
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data"></param>
        /// <param name="bWaitForReply"></param>
        /// <returns></returns>
        public bool Send(byte[] data, bool bWaitForReply = false)
        {
            try
            {
                m_streamToClient.Write(data, 0, data.Length);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }

            return true;
        }

        /// <summary>
        /// 检测是否在线
        /// </summary>
        void CheckForConnection()
        {
            bool bIsDisConnected = false;

            while (true)
            {
                try
                {
                    bIsDisConnected = m_client.Client.Poll(1, SelectMode.SelectRead) && (0 == m_client.Client.Available);
                }
                catch (SocketException)
                {
                    bIsDisConnected = true;
                }

                if (bIsDisConnected && (null != m_doDisConnect))
                {
                    m_doDisConnect(this);
                    break;
                }

                Thread.Sleep(100);
            }
        }

        /// <summary>
        /// 接收数据完成时回调函数
        /// </summary>
        /// <param name="ar"></param>
        private void ReadComplete(IAsyncResult ar)
        {
            try
            {
                var bytesRead = 0;
                lock (m_streamToClient)
                {
                    bytesRead = m_streamToClient.EndRead(ar);
                    Console.WriteLine("Reading data, {0} bytes ...", bytesRead);
                }

                // 返回0字节，说明远程主机关闭了连接
                if (bytesRead == 0)
                {
                    throw new Exception("读取到0字节");
                }

                #region 处理接收的数据

                // 创建一个数据缓冲
                byte[] tmp = new byte[bytesRead];
                // 先读到缓冲
                Array.Copy(m_buffer, tmp, bytesRead);
                // 然后交给用户处理
                if (null != m_doReceiveData)
                {
                    TCPClientResult result = new TCPClientResult(this, m_recvBuffer);
                    m_doReceiveData(result);

                    // 用户处理完毕后，检查一下是否需要保持数据
                    // 需要，则说明用户收到的数据不完整，还需要继续接收数据，直到数据完整后再处理
                    // 不需要，则直接清空接收缓存
                    // 特殊情况：接收数据的缓存数据量太大，为了提高性能，直接清空接收缓存

                    if (!result.bKeepData)
                    {
                        m_recvBuffer.Clear();
                    }

                    if (BufferSize < result.data.Count)
                    {
                        m_recvBuffer.Clear();
                    }
                }

                #endregion

                // 清理缓存，避免脏读
                Array.Clear(m_buffer, 0, m_buffer.Length);

                // 再次调用BeginRead()，准备下一次接收
                lock (m_streamToClient)
                {
                    var callBack = new AsyncCallback(ReadComplete);
                    m_streamToClient.BeginRead(m_buffer, 0, BufferSize, callBack, null);
                }
            }
            catch (Exception ex)
            {
                // 捕获异常时退出程序
                Console.WriteLine(ex.Message);
                //m_doDisConnect?.Invoke(this);
                if (m_doDisConnect != null) {
                    m_doDisConnect.Invoke(this);
                }
            }
        }

        public void Dispose()
        {
            try
            {
                m_streamToClient.Dispose();
                m_client.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }

    /// <summary>
    /// TCP客户端数据结果
    /// </summary>
    public class TCPClientResult
    {
        public TCPClientResult(TCPClient tcpClient, List<byte> data)
        {
            client = tcpClient;
            this.data = data;
        }

        /// <summary>
        /// 发送数据的TCPClient
        /// </summary>
        public TCPClient client { get; set; }

        /// <summary>
        /// 待处理的数据
        /// </summary>
        public List<byte> data { get; set; }

        /// <summary>
        /// 数据是否需要缓存
        /// </summary>
        public bool bKeepData { get; set; }
    }
}
